﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Intorise.Common;
using Model;
using System.Net;
using Intorise.Net;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Configuration;
using System.Threading;
using Config;
using System.Xml.Serialization;
using System.IO;
using System.Collections;
using System.Web;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Net.Sockets;

namespace LoginConsole
{
    class Program
    {
        static readonly string LOGIN_HANDLER_URL = ConfigurationManager.AppSettings["LoginHandlerUrl"];
        static readonly string LOGIN_COMPLETE_HANDLER_URL = ConfigurationManager.AppSettings["LoginCompleteHandlerUrl"];
        static object resetNetLocker = new object();
        static bool needResetNet = false;
        static List<LoginEntity> entities = new List<LoginEntity>();
        static Adsl adsl = null;
        static NetTools net = new NetTools(30);
        static byte[] headData = new byte[] { 0x00, 0x01, 0x87, 0x70 };
        static byte[] noCharacterData = new byte[] { 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x05, 0x00, 0x00, 0x00, 0x00 };
        static int timeout = 30;//建立人物的超时秒
        static string ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

        static string __rsa(string str)
        {
            var len = str.Length; var i = 0; var out0 = ""; 
            while (i < len)
            {
                var c1 = str[i++] & 0xff;
                if (i == len) { out0 += ch[c1 >> 2]; out0 += ch[(c1 & 0x3) << 4]; out0 += "=="; break; }
                var c2 = str[i++];
                if (i == len)
                {
                    out0 += ch[c1 >> 2]; out0 += ch[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)]; out0 += ch[((c2 & 0xF) << 2)]; out0 += "="; break;
                }
                var c3 = str[(i++)]; out0 += ch[(c1 >> 2)]; out0 += ch[(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4))]; out0 += ch[(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6))];
                out0 += ch[(c3 & 0x3F)];
            }
            return out0;
        }

        /// <summary>
        /// 加密密码
        /// </summary>
        /// <param name="pwd"></param>
        /// <returns></returns>
        static string Encrypt(string pwd)
        {
            var maxPos = ch.Length - 2;
            Random random = new Random();
            random.NextDouble();
            var w = new List<string>();
            for (var i = 0; i < 15; i++)
            {
                w.Add(ch[((int)Math.Floor(random.NextDouble() * maxPos))].ToString());
                if (i == 7) { w.Add(pwd.Substring(0, 3)); }
                if (i == 12) { w.Add(pwd.Substring(3)); }
            }
            return __rsa(string.Join("", w));
        }

        static void Main(string[] args)
        {
            JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
            {
                var settings = new JsonSerializerSettings();
                settings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                settings.NullValueHandling = NullValueHandling.Ignore;
                settings.Culture = new System.Globalization.CultureInfo("zh-cn");
                settings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore;
                settings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                return settings;
            });

            ConfigManager.ApplicationConfig = new MainConfig();
            ConfigManager.BusinessConfig = new BotConfig();
            ConfigManager.Init();
            adsl = new Adsl();
            adsl.IsCheckNetStatus = true;
            adsl.IsCheckIp = true;
            adsl.CheckNetStatusCount = 3;
            adsl.ConnectDelay = 5;//adsl断线后等待时间(秒)

            ConsoleBLL.WriteLineInfo("登录服务器已启动...");

            while (true)
            {
                HttpWebRequest request = null;
                HttpWebResponse response = null;
                try
                {
                    request = net.SendRequest(LOGIN_HANDLER_URL, "GET");
                    response = (HttpWebResponse)request.GetResponse();
                    var html = net.GetResponseHtml(response);
                    try
                    {
                        JObject result = (JObject)JsonConvert.DeserializeObject(html);
                        if (result["Success"].ToString() == "True")
                        {
                            var loginEntry = JsonConvert.DeserializeObject<LoginEntity>(result["Data"].ToString());
                            lock (entities)
                            {
                                if (entities.Count(c => c.Account == loginEntry.Account && c.ServerNo == loginEntry.ServerNo) > 0)
                                {
                                    ConsoleBLL.WriteLineWaring("获取到了已经在登录的账号：" + loginEntry);
                                    continue;
                                }
                            }
                            ConsoleBLL.WriteLineInfo("需要登录的账号：" + loginEntry.ServerNo + "\t" + loginEntry.Account);
                            try
                            {
                                if (Login(loginEntry))
                                {
                                    ConsoleBLL.WriteLineInfo(loginEntry.ServerNo + "\t" + loginEntry.Account + " 登录成功！");
                                    CreateCharacter(loginEntry);
                                }
                                else
                                {
                                    ConsoleBLL.WriteLineError(loginEntry.ServerNo + "\t" + loginEntry.Account + " 登录失败！");
                                }
                            }
                            catch (Exception ex)
                            {
                                ConsoleBLL.WriteLineError("登录时出错：" + ex.Message);
                            }
                            ProcessLoginResult(loginEntry);
                        }
                        else
                        {
                            ConsoleBLL.WriteLine(result["Message"].ToString());
                            Thread.Sleep(3000);
                        }
                    }
                    catch (Exception ex)
                    {
                        ConsoleBLL.WriteLineError(ex.Message);
                    }

                    if (!ProcessReconnect())
                    {
                        ConsoleBLL.WriteLineError("重连网络失败，退出...");
                        break;//退出
                    }
                }
                catch (Exception ex)
                {
                    ConsoleBLL.WriteLineError(ex.Message);
                    Thread.Sleep(3000);
                }
                finally
                {
                    if (request != null)
                    {
                        request.Abort();
                    }
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }//end while
            ConsoleBLL.WriteLineInfo("登录服务器已停止...按回车键退出程序...");
            Console.ReadLine();
        }

        public static void CreateCharacter(LoginEntity player)
        {
            try
            {
                if (player.LoginResult != LoginResultEnum.Success)
                {
                    return;
                }
                TcpClient Client = null;
                TcpClient Client0 = null;
                try
                {
                    Client0 = new TcpClient(player.ServerIP, 843);
                    var data = Tools.GetAscII("<policy-file-request/>\0");
                    net.SendData(Client0, data);
                    Client0.Close();

                    Client = new TcpClient(player.ServerIP, player.Port);
                    data = Tools.Contact(headData, DataPackager.GetNetworkData((short)player.ServerNo.Length), Tools.GetAscII(player.ServerNo),
                        net.GetNetworkBytes((short)player.Account.Length), Tools.GetAscII(player.Account), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A }
                        , Tools.GetAscII(player.TicketTime), new byte[] { 0x00, 0x01, 0x33, 0x00, 0x20 }, Tools.GetAscII(player.Sign)
                        , new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x00, 0x01, 0x31, 0x00, 0x01, 0x30 });
                    data = Tools.Contact(net.GetNetworkBytes(data.Length), data);
                    net.SendData(Client, data, data.Length);
                    byte[] buffer = new byte[10240];
                    var stream = Client.GetStream();
                    stream.ReadTimeout = 10 * 1000;//10秒
                    stream.WriteTimeout = 10 * 1000;
                    int length = 0;
                    DateTime begin = DateTime.Now;

                    do
                    {
                        if ((DateTime.Now - begin).TotalSeconds > timeout)
                        {
                            player.LoginResult = LoginResultEnum.Failure;
                            ConsoleBLL.WriteLineError("接收人物数据超时！"); return;
                        }
                        length = stream.Read(buffer, 0, 4);//读取4个字节为接下来的数据包的长度
                        if (length < 4)
                        {
                            continue;
                        }

                        length = net.GetHostInt32(buffer, 0);
                        length = stream.Read(buffer, 0, length);
                        ConsoleBLL.WriteLineWaring(length + "\t" + Tools.GetHexString(buffer, length));

                        if (length > 4)
                        {
                            if (buffer[0] == 0x00 && buffer[1] == 0x01 && buffer[2] == 0x87 && buffer[3] == 0x0A)//封号或限制登录
                            {
                                ConsoleBLL.WriteLineError("账号已被封");
                                player.LoginResult = LoginResultEnum.Banned; return;
                            }
                            if (buffer[0] == 0x00 && buffer[1] == 0x01 && buffer[2] == 0x87 && buffer[3] == 0x05)//人物数据
                            {
                                if (length == 8) //如果是00 00 00 08 00 01 87 05 00 00 00 00 则表示没有人物
                                {
                                    ConsoleBLL.WriteLineInfo("没有建立人物，开始建立人物..."); break;
                                }
                                else
                                {
                                    ConsoleBLL.WriteLineInfo("已经有人物！");
                                    player.LoginResult = LoginResultEnum.Success;
                                    return;
                                }
                            }
                        }
                        Thread.Sleep(1000);
                    } while (true);

                    net.SendData(Client, new byte[] { 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x87, 0x6E, 0x00 });
                    Thread.Sleep(100);
                    int size = stream.Read(buffer, 0, buffer.Length);
                    ConsoleBLL.WriteLineWaring(Tools.GetHexString(buffer, size));

                    string name = GameHelper.GetName();
                    data = DataPackager.GetCreateCharacterData(name);
                    net.SendData(Client, data);
                    Thread.Sleep(100);
                    size = stream.Read(buffer, 0, buffer.Length);
                    if (size > 0)
                    {
                        if (Tools.GetString(buffer, size).Contains(name))
                        {
                            ConsoleBLL.WriteLineInfo("建立人物成功：" + name);
                            player.LoginResult = LoginResultEnum.Success;
                            return;
                        }
                    }

                    player.LoginResult = LoginResultEnum.Failure;
                    ConsoleBLL.WriteLineInfo("建立人物失败！");
                }
                catch (Exception ex)
                {
                    player.LoginResult = LoginResultEnum.Failure;
                    ConsoleBLL.WriteLineWaring("出错：" + ex.Message);
                }
                finally
                {
                    if (Client != null && Client.Connected)
                    {
                        Client.Close();
                    }
                    if (Client0 != null && Client0.Connected)
                    {
                        Client0.Close();
                    }
                }
            }
            finally
            {
                ConsoleBLL.WriteLine("创建人物已执行完毕");
            }
        }

        public static bool Login(LoginEntity player)
        {
            NetTools net = new NetTools();
            //Adsl adsl = new Adsl();

            player.LoginResult = LoginResultEnum.Failure;
            string loginUrl = "https://my.37.com/api/login.php?action=login&login_account={0}&password={1}&ajax=0&remember_me=1&save_state=0&ltype=1&s=1";
            string enterGameUrl = "http://gameapp.37.com/controller/enter_game.php?game_id=237&sid={0}&showlogintype=1&refer=37wancom";
            //string registerUrl = "http://api.dts.37wan.com/client/?user_name={0}&is_adult=1&sign={1}&server_id=S{2}&client=1&channel=0&real_user=1&user_info=1&tg_pass=0&time={3}";
            string registerServerUrl = null;

            var cookie = new CookieContainer();
            player.CookieContainer = cookie;
            ConsoleBLL.WriteLine(player + " 开始登录...");

            string codeData = "";

            var request = net.SendRequest(string.Format(loginUrl, player.Account, Encrypt(player.Password)) + codeData, "GET", cookie);
            var response = (HttpWebResponse)request.GetResponse();
            var html = net.GetResponseHtmlGzip(response).Replace("callback(","").Replace(")","");
            response.Close();
            JObject result = (JObject)JsonConvert.DeserializeObject(html);

            var flag = result["code"].ToString();
            switch (flag)
            {
                case "-7":
                case "-11":
                    ConsoleBLL.WriteLineError(player + " 出现验证码，重新拨号");
                    player.LoginResult = LoginResultEnum.NeedCode;
                    return false;
                case "-2":
                    ConsoleBLL.WriteLineError(player + " 登录失败，密码错误");
                    ConsoleBLL.WriteLine(html.ToString());
                    player.LoginResult = LoginResultEnum.WrongPassword;
                    return false;
                case "-1":
                    ConsoleBLL.WriteLineError(player + " 登录失败，账户名错误");
                    ConsoleBLL.WriteLine(html.ToString());
                    player.LoginResult = LoginResultEnum.WrongPassword;
                    return false;
                case "-10":
                    ConsoleBLL.WriteLineError(player + " 登录尝试次数过多");
                    ConsoleBLL.WriteLine(html.ToString());
                    player.LoginResult = LoginResultEnum.TryTooManyTimes;
                    return false;
            }

            request = net.SendRequest(string.Format(enterGameUrl, player.ServerNo), "GET", cookie);

            response = (HttpWebResponse)request.GetResponse();
            html = net.GetResponseHtmlGzip(response);
            response.Close();
            registerServerUrl = response.Headers["location"];

            if (registerServerUrl == null || html.Contains("验证码"))
            {
                player.LoginResult = LoginResultEnum.NeedCode;
                return false;
            }

            #region 获取cookie
            request = net.SendRequest("https://my.37.com/httpsEnable.gif?t=1449296460816", "GET", cookie);
            response = (HttpWebResponse)request.GetResponse();
            response.Close();
            request = net.SendRequest("http://cm.he2d.com/1/", "GET", cookie);
            response = (HttpWebResponse)request.GetResponse();
            response.Close();

            request = net.SendRequest("http://my.37.com/api/message_api.php?action=is_remind&callback=aaa&_=1449297015903", "GET", cookie);
            response = (HttpWebResponse)request.GetResponse();
            response.Close();
            #endregion

            request = net.SendRequest(registerServerUrl, "GET", cookie);
            response = (HttpWebResponse)request.GetResponse();
            html = net.GetResponseHtmlGzip(response);
            //response.Close();

            var port = GameHelper.GetPort(html);
            if (port == 0)
            {
                ConsoleBLL.WriteLineError(player + " 无法获取服务器端口，html:");
                ConsoleBLL.WriteLine(html);
                return false;
            }


            player.Port = port;
            player.TicketTime = GameHelper.GetTime(html);
            player.Sign = GameHelper.GetSign(html);
            player.ServerIP = GameHelper.GetIP(html);
            player.LoginResult = LoginResultEnum.Success;
            return true;
        }

        static void ProcessLoginResult(LoginEntity login)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            try
            {
                entities.Remove(login);
                ConsoleBLL.WriteLine(login + " 登录结果：" + login.LoginResult + "\t" + login.LoginStatus);

                if (login.LoginResult == LoginResultEnum.NeedCode)
                {
                    if (!needResetNet)
                    {
                        lock (resetNetLocker) { needResetNet = true; }//需要重新拨号
                    }
                }

                NetTools net = new NetTools();

                login.Cookies = GetAllCookies(login.CookieContainer);
                var json = JsonConvert.SerializeObject(login);


                request = net.SendRequest(LOGIN_COMPLETE_HANDLER_URL, "GET");
                net.PostData("data="+HttpUtility.UrlEncode(json),request);
                response = (HttpWebResponse)request.GetResponse();
                var html = net.GetResponseHtml(response);
            }
            finally
            {
                if (request != null)
                {
                    request.Abort();
                }
                if (response != null)
                {
                    response.Close();
                }
                ConsoleBLL.WriteLine("登录结果发送完毕");
            }
        }

        static bool ProcessReconnect()
        {
            lock (resetNetLocker)
            {
                if (needResetNet)
                {
                    while (entities.Count > 0)
                    {
                        ConsoleBLL.WriteLineWaring("等待当前账号完成登录...");
                        Thread.Sleep(1000);
                    }
                    ConsoleBLL.WriteLineWaring("重连ADSL网络...");
                    int count = 0;
                    while (true)
                    {
                        if (adsl.ConnectAndCheckIP())
                        {
                            ConsoleBLL.WriteLineInfo("网络已联通");
                            needResetNet = false;
                            return true;
                        }
                        else
                        {
                            ConsoleBLL.WriteLineInfo("网络未联通，3秒后重连ADSL网络...");
                            Thread.Sleep(1000);
                            count++;
                        }
                        if (count > 3)
                        {
                            ConsoleBLL.WriteLineInfo("超过3次没有联通网络，重新启动程序！");
                            ConsoleBLL.WriteLineWaring("重新启动程序...");
                            Task.Factory.StartNew(() =>
                            {
                                Process p = new Process();
                                p.StartInfo = new ProcessStartInfo(AppDomain.CurrentDomain.BaseDirectory + "LoginConsole.exe");
                                p.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
                                p.Start();
                            });
                            needResetNet = false;
                            return false;
                        }
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 遍历CookieContainer
        /// </summary>
        /// <param name="cc"></param>
        /// <returns></returns>
        public static List<Cookie> GetAllCookies(CookieContainer cc)
        {
            List<Cookie> lstCookies = new List<Cookie>();

            Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable",
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField |
                System.Reflection.BindingFlags.Instance, null, cc, new object[] { });

            foreach (object pathList in table.Values)
            {
                SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list",
                    System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField
                    | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { });
                foreach (CookieCollection colCookies in lstCookieCol.Values)
                    foreach (Cookie c in colCookies) lstCookies.Add(c);
            }
            return lstCookies;
        }
    }
}
